SNFSmartSync = { }  -- this is the public global object, used as a namespace

local todoq = { } -- this is a private local object.  Stores pending actions


local version = "3"
--[[
Typical target conversations:

init:  "List Raids: <list from safe senders>"
reply: if receiver is in that list, then they respond:  "I have <RaidId>,<RaidID>,etc" -- repeat for each raidid they have.
init:  compares this list against his own.  For each that is reported, but init doesn't have:
init:  "show me raid <raidid>"
init:  "show me auctions: <raidid>"
reply: "raid sync data"
reply: "auction sync data"


--]]--
--[[
Design aspects of conversations:

build list of safe senders from options
init:  "List Raids: {list from safe senders}"
reply: if receiver is in that list, then they respond:  "I have {<RaidId>,<RaidID>,etc]" -- repeat for each raidid they have.
init:  compares this list against his own.  For each that is reported, but init doesn't have:
init:  "show me raid <raidid>"
init:  "show me auctions: <raidid>"
reply: "raid sync data"
reply: "auction sync data"


--]]--

--Might be able to get rid of this method now - replaced by QueueSyncUnique
function SNFSmartSync:QueueSync(datatype, raidid)

	if not todoq then todoq = { } end

	local item = { }
	item["type"] = datatype
	item["raidid"] = raidid

	table.insert(todoq, item)
end

function SNFSmartSync:QueueSyncUnique(datatype, raidid)

	if not todoq then todoq = { } end

	for t, r in pairs(todoq) do
		if t == datatype and r == raidi then
			SNFDEBUG(3, "Don't need to queue "..(t or "nil").."/"..(r or "nil"))
			return
		end
	end
	local item = { }
	item["type"] = datatype
	item["raidid"] = raidid

	table.insert(todoq, item)
end

function SNFSmartSync:CheckToDoQueue()

	local item = table.remove(todoq)

	if item and item["raidid"] then
		SNFDEBUG(2, "Dequeued item: "..(item["type"] or "nil").." for raid "..item["raidid"].." left: "..#(todoq))
		if item["type"] == "raids" then
			SNFSmartSync:SyncRaid(item["raidid"], SNFAttend:GetData(item["raidid"]))
		elseif item["type"] == "auctions" then
			SNFSmartSync:SyncAuctions(item["raidid"], SNFGetAuctionData(item["raidid"]))
		end
	end
end


function SNFSmartSync:ParseMessage(msg, sender)
	SNFDEBUG(4, "Rec SS: "..gsub(msg, "\124", "\124\124"))

	if (not SNFCheckTrustSync(sender)) then	return end

	local _, vers, category, raidid, data = strmatch(msg, "(.-)|v(.-)|(.-)|(.-)|(.*)")

	if not (vers and category and raidid and data) then

		SNFDEBUG(2, "Invalid smart sync message: "..(msg or "nil"))
		return
	end
	if vers ~= version then
		SNFDEBUG(2, "Skipping version "..(vers or "nil").." smart sync message")
		return
	end
	SNFDEBUG(3, "Rec SS: cat "..category..", raid: "..raidid)

	if (category == "RS") then
	--Message looks like:  "SYNC_SMART|v3|RS|<RaidID>|<RaidLeader>|<Source>|<StartTime>|<EndTime>|<Authority>|"
		local leader, source, starttime, endtime, auth = strmatch(data, "(.-)|(.-)|(.-)|(.-)|(.*)|")
		if not(leader and source and starttime and endtime and auth) then
			SNFDEBUG(2, "Invalid RS message: "..(data or "nil"))
			return
		end
		SNFAttend:SetRaidSum(raidid, leader, source, tonumber(auth), starttime, endtime)
	elseif (category == "RL") then
	--List Messages looks like:  "SYNC_SMART|v2|RL|<RaidID>|StartList|{Name1,Name2,ETC}|"
	--List Messages looks like:  "SYNC_SMART|v2|RL|<RaidID>|EndList|{Name1,Name2,ETC}|"
	--List Messages looks like:  "SYNC_SMART|v2|RL|<RaidID>|WaitList|{Name1,Name2,ETC}|"
		local whichlist, listdata = strmatch(data, "(.-)|(.*)|")
		if not(whichlist and listdata) then
			SNFDEBUG(2, "Invalid RL message: "..(data or "nil"))
			return
		end
		SNFAttend:AddRaidList(raidid, whichlist, listdata)
	elseif (category == "AS") then
	--Message looks like:  "SYNC_SMART|v2|AS|<RaidID>|<AuctionNum>|<AuctionItem>|<winner>|<winner bidlevel>|<MasterLooter>|"
		local i, itemid, winner, winnerbid, looter = strmatch(data, "(.-)|(.-)|(.-)|(.-)|(.*)|")
		if not(i and itemid and winner and winnerbid and looter) then
			SNFDEBUG(2, "Invalid AS message: "..(data or "nil"))
			return
		end
		SNFSetAuctionSum(raidid, tonumber(i), gsub(itemid,  "~", "\124"), winner, winnerbid, looter)
	elseif (category == "AutoB") then
		if (raidid) then
		--Message looks like:  "SYNC_SMART|v3|AutoB|<RaidID>:checksum:checksum,<RaidID>:checksum:checksum,<RaidID>:checksum:checksum,|"
			SNFDEBUG(3, "Received AutoB request")
			return SNFSmartSync:RespondtoBroadcast(raidid)
		end
	elseif (category == "AutoR") then
		SNFDEBUG(3, "Received AutoR request")
	--Message looks like:  "SYNC_SMART|v2|AutoR|<RaidID>|"
		if SNFAttendData and SNFAttendData[raidid] then
			SNFSmartSync:QueueSyncUnique("raids", raidid)
		else
			SNFDEBUG(0, "Not autosyncing raid info for raid "..(raidid or "nil"))
		end
		if SNFAuctions and SNFAuctions[raidid] then
			SNFSmartSync:QueueSyncUnique("auctions", raidid)
		else
			SNFDEBUG(0, "Not autosyncing auction info for raid "..(raidid or "nil"))
		end

--[[	elseif (category == "BL") then

		local i, itemid, winner, winnerbid, looter = strmatch(data, "(.-)|(.*)|")
		if not(i and itemid and winner and winnerbid and looter) then
			SNFDEBUG(2, "Invalid AS message: "..(data or "nil"))
			return
		end
		SNFSetAuctionSum(raidid, i, itemid, winner, winnerbid, looter)
]]
	end


end

local MSG_LEN_LIMIT = 200

local function GenerateList(prefix, list, trailer)

	local n = 1
	local useful = false
	local m = prefix

--	SNFDEBUG(4, "GenerateList("..(prefix or "nil")..","..(type(list or "nil"))..","..(trailer or "nil")..")")
	if not prefix then
		SNFDEBUG(1, "GenerateList - prefix is nil, aborting")
		return
	end

	if not list then
		SNFDEBUG(1, "GenerateList - list is nil, aborting")
		SNFDEBUG(2, "prefix was "..prefix)
		return
	end

	if type(list) ~= "table" then
		SNFDEBUG(1, "GenerateList - list is not a table, aborting")
		SNFPRINT(0, "list", list, 1)
		return
	end

	if not trailer then
		SNFDEBUG(1, "GenerateList - trailer is nil, aborting")
		return
	end

	while n <= #(list) do
		local item = list[n]..","
		if strlen(m) + strlen(item) + strlen(trailer) > MSG_LEN_LIMIT then
			if (useful) then
				SNFMessage:Queue(m..trailer)
			end
			m = prefix
			useful = false;
		else
			m = m..item
			useful = true
			n = n + 1
		end
	end
	if (useful) then -- hit end of list without hitting size limit, so send this one off
		SNFMessage:Queue(m..trailer)
	end
end

function SNFSmartSync:SyncRaid(raidid, raiddata)

	if not raidid or not raiddata then
		SNFDEBUG(3, "No raidid or raiddata sent to SyncRaid, aborting sync")
		return false
	end

	-- now need to determine what data to send on to the other side.
	--Message looks like:  "SYNC_SMART|v3|RS|<RaidID>|<RaidLeader>|<Source>|<StartTime>|<EndTime>|<Authority>|"
	local m = "SYNC_SMART|v"..version.."|RS|"..raidid.."|"..(raiddata["RaidLeader"] or "nil").."|"..
			(raiddata["Source"] or "nil").."|"..(raiddata["StartTime"] or "").."|"..(raiddata["EndTime"] or "")..
			"|"..(raiddata["Authority"] or "1").."|"

	SNFMessage:Queue(m)

	--List Messages looks like:  "SYNC_SMART|v3|RL|<RaidID>|StartList|{Name1,Name2,ETC}|"
	--List Messages looks like:  "SYNC_SMART|v3|RL|<RaidID>|EndList|{Name1,Name2,ETC}|"
	--List Messages looks like:  "SYNC_SMART|v3|RL|<RaidID>|WaitList|{Name1,Name2,ETC}|"
	m = "SYNC_SMART|v"..version.."|RL|"..raidid.."|start_in|"
	if raiddata["start_in"] then GenerateList(m, raiddata["start_in"], "|") end

	m = "SYNC_SMART|v"..version.."|RL|"..raidid.."|end_in|"
	if raiddata["end_in"] then GenerateList(m, raiddata["end_in"], "|") end

	m = "SYNC_SMART|v"..version.."|RL|"..raidid.."|wait_list|"
	if raiddata["wait_list"] then GenerateList(m, raiddata["wait_list"], "|") end

	m = "SYNC_SMART|v"..version.."|RL|"..raidid.."|start_wait|"
	if raiddata["start_wait"] then GenerateList(m, raiddata["start_wait"], "|") end

	m = "SYNC_SMART|v"..version.."|RL|"..raidid.."|end_wait|"
	if raiddata["end_wait"] then GenerateList(m, raiddata["end_wait"], "|") end

	if #(msgq) > 0 then
		SNFDEBUG(3, "Messages queued: "..#(msgq))

	end
	if #(parseq) > 0 then
		SNFDEBUG(2, "Parse queue size: "..#(parseq))
	end

end

function SNFSmartSync:SyncAuctions(raidid, auctions)

	if not raidid or not auctions then
		SNFDEBUG(2, "No raidid or auction data sent to SyncAuctions, aborting sync")
		return false
	end

	-- now need to determine what data to send on to the other side.
	--Message looks like:  "SYNC_SMART|v2|AS|<RaidID>|<AuctionNum>|<AuctionItem (itemid only)>|<winner>|<winner bidlevel>|<MasterLooter>|"

	for i, data in ipairs(auctions) do
		local itemid

		if data["AuctionItem"] then
			itemid = gsub(data["AuctionItem"], "\124", "~")
		else
			itemid = ""
		end

--		if strlen(itemid) > 10 then
--			itemid = SNFGetItemID(itemid)
--		end
--		if not itemid or strlen(itemid) == 0 then
--			SNFDEBUG(1, "Failed to parse itemid from "..(data["AuctionItem"] or "nil"))
--		else
			local bidlevel = ""
			if data["BidList"] and data["winner"] and data["BidList"][data["winner"]] then
				bidlevel = data["BidList"][data["winner"]]["level"]
			end
			if not bidlevel then bidlevel = "" end
			local m = "SYNC_SMART|v"..version.."|AS|"..raidid.."|"..i.."|"..
				itemid.."|"..(data["winner"] or "").."|"..bidlevel..
				"|"..data["MasterLooter"].."|"

			SNFMessage:Queue(m)
--		end

	end
	if #(msgq) > 0 then
		SNFDEBUG(3, "Messages queued: "..#(msgq))

	end
	if #(parseq) > 0 then
		SNFDEBUG(2, "Parse queue size: "..#(parseq))
	end

end


function SNFSmartSync:RaidSync(raidid)
	if not parseq then parseq = { } end
	if not msgq then msgq = { } end
	if not msgcount then msgcount = 0 end

	if raidid == "all" then
		for r, _ in pairs(SNFAttendData) do
			if not r then
				SNFDEBUG(0, "Nil found when scanning raids to sync")
			else
				if SNFAttendData and SNFAttendData[r] then
					SNFSmartSync:QueueSyncUnique("raids", r)
				else
					SNFDEBUG(0, "Not syncing raid info for raid "..(r or "nil"))
				end
				if SNFAuctions and SNFAuctions[r] then
					SNFSmartSync:QueueSyncUnique("auctions", r)
				else
					SNFDEBUG(0, "Not syncing auction info for raid "..(r or "nil"))
				end
			end
		end
	else
		if SNFAttendData and SNFAttendData[raidid] then
			SNFSmartSync:QueueSyncUnique("raids", raidid)
		else
			SNFDEBUG(0, "Not syncing raid info for raid "..(raidid or "nil"))
		end
		if SNFAuctions and SNFAuctions[raidid] then
			SNFSmartSync:QueueSyncUnique("auctions", raidid)
		else
			SNFDEBUG(0, "Not syncing auction info for raid "..(raidid or "nil"))
		end
	end
end

function SNFSmartSync:StartAutoSync()

	--Sync Messages looks like:  "SYNC_SMART|v3|AutoB|20100101,20100102,20100103|"  --etc
	--New Sync Messages looks like:  "SYNC_SMART|v3|AutoB|20100101:CHECKSUM,20100102:CHECKSUM,20100103:CHECKSUM|"  --etc
	--Sync Messages looks like:  "SYNC_SMART|v3|AutoR|<RaidID>"
	local m = "SYNC_SMART|v"..version.."|AutoB|"

	local raidlist = SNFAttend:BuildListofRaids()

	if #(raidlist) > 0 then
		GenerateList(m, raidlist, "|")
	else
		SNFDEBUG(0, "No raids available to sync")
		return 0
	end

	SNFDEBUG(3, "Queueing broadcast message: ")

	--TEMPORARY
	--SNFSmartSync:RespondtoBroadcast("20100401:35294:0,|")
end

function SNFSmartSync:RespondtoBroadcast(data)

	local need = SNFAttend:FindMissingRaids(data)

	if #(need) > 0 then
		for _, DATE in ipairs(need) do
			local m = "SYNC_SMART|v"..version.."|AutoR|"..DATE.."|"
			SNFMessage:Queue(m)
		end
	else
		SNFDEBUG(3, "Didn't need to request any raids of: "..(data or "nil"))
	end
end

